package net.alche.fa.core;

import java.util.List;
import net.alche.fa.core.model.Logic;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import net.alche.al.util.AC;
import net.alche.al.util.Far;
import net.alche.al.util.M;
import net.alche.fa.core.model.FaValue;
import net.alche.fa.core.model.FaParam;
import net.alche.fa.core.model.FaConf;

@SuppressWarnings("all")
public class FaClassConf {
  public FaPlatform plat;
  public String name; // 类名称
  public String extendName; // 继承对象名称
  public List<String> implNameList; // 继承接口列表
  public String packagePath;
  public List<Logic> initLogicList; // 对象初始化代码
  public List<Logic> staticLogicList; // 静态初始化代码
  public Map<String, String> def = new HashMap<>(); // 类注解
  public Map<String, Map<String, String>> varDef = new HashMap<>();
  public List<FaMethod> methodList = new ArrayList<>();
  public SageStone staticSage; // 静态对象
  public static Map<String, String> aliaMap;
  public Map<String, FaClassConf> inner; // 内部类

  public FaClassConf() {
    if (aliaMap == null) {
      aliaMap = new HashMap<>();
      String[] langKeys = new String[] { "char Character", "byte Byte", "short Short", "int Integer", "long Long",
          "float Float", "double Double", "boolean Boolean" };
      for (String key : langKeys) {
        String[] sp = key.split(" ");
        aliaMap.put(sp[0], sp[1]);
        aliaMap.put(sp[1], sp[0]);
      }
    }
  }

  public FaClassConf(FaPlatform plat) {
    this.plat = plat;
  }

  public void replaceClass(FaClassConf newClass) {
    this.name = newClass.getName();
    this.initLogicList = newClass.getInitLogicList();
    this.def = newClass.getDef();
    this.varDef = newClass.getVarDef();
    this.methodList = newClass.getMethodList();
  }

  // 获取类型的定义变量列表
  public List<String> getClassVarNameList() {
    if (initLogicList == null || initLogicList.isEmpty()) {
      return null;
    }
    List<String> varNameList = new ArrayList<>();
    for (Logic g : initLogicList) {
      String what = g.getWhat();
      if (what != null && what.startsWith("var-")) {
        varNameList.add(g.getData());
      }
    }
    return varNameList;
  }

  public void fillAtMap(Map<String, String> mapAt, Logic atLogic) {
    Logic dataLogic = atLogic.getLogicData();
    if (dataLogic == null) {
      if (AC.notEmpty(atLogic.getData())) {
        mapAt.put(atLogic.getData(), "1");
      }
      return;
    }
    String atName = dataLogic.getObj();
    mapAt.put(atName, "1");
    fillAtAttr(mapAt, dataLogic.getLogicData(), atName);
  }

  private void fillAtAttr(Map<String, String> mapAt, Logic attrLogic, String atName) {
    if (attrLogic == null) {
      return;
    }
    Logic objLogic = attrLogic.getLogicObj();
    if (objLogic != null && objLogic.getFaDataValue() != null) {
      String key = objLogic.getObj();
      String value = objLogic.getFaDataValue().getStrValue();
      mapAt.put(atName + "." + key, value);
    }
    fillAtAttr(mapAt, attrLogic.getLogicData(), atName);
  }

  public String getDotPath(Logic logic, String str) {
    if (logic.getPlus() != null) {
      int index = 0;
      for (Logic gg : logic.getPlus()) {
        if (index > 0) {
          str += gg.getWhat() != null ? gg.getWhat() : gg.getObj();
        }
        index++;
      }
    }
    return str;
  }

  // 查找方法
  public FaMethod getMethod(String methodName, List<FaValue> params) throws FaException {
    int parLength = params != null ? params.size() : 0;
    for (FaMethod method : this.methodList) {
      if (methodName.equals(method.getMethodName())) {
        // if (M.equal("doSelectOne", methodName)) {
        // Far.log("null");
        // }
        // 定量参数
        if (parLength == method.getParamList().size()) {
          if (parLength == 0) {
            return method;
          }
          boolean isOk = true;
          for (int i = 0; i < parLength; i++) {
            FaParam p = method.getParamList().get(i);
            FaValue val = params.get(i);
            if (val == null) {
              throw new FaException(FaException.LESS_METHOD,
                  "[FaClassConf.getMethod]: " + this.name + "." + methodName + "( " + p.varName + " is null. )");
            }
            if (val.isNull() && val.getObjType() == null) {
            } else if (!this.compareGive(val.getObjType(), p.objTypeName)) {
              isOk = false;
              if (val.getObjValue() != null) {
                Class cls = val.getObjValue().getClass();
                if (this.compareGiveFromJavaObj(cls.getSuperclass(), p.objTypeName)) {
                  isOk = true;
                }
              }
              if (!isOk)
                break;
            }
          }
          if (isOk) {
            return method;
          }
        } else if (method.getParamList().size() > 0) {
          FaParam last = method.paramList.get(method.paramList.size() - 1);
          if (last.isDynamic && this.compreDynamicParam(method, params)) {
            return method;
          }
        }
      }
    }
    if (this.extendName != null) {
      FaClassConf fcc = this.plat.getClassConf(this.extendName);
      if (fcc != null) {
        return fcc.getMethod(methodName, params);
      }
    }
    return null;
  }

  // 对比动态参数
  public boolean compreDynamicParam(FaMethod method, List<FaValue> params) {
    int size = params.size();
    int pSize = method.getParamList().size();
    for (int i = 0; i < size; i++) {
      FaValue fv = params.get(i);
      FaParam p = null;
      if (pSize <= i) {
        p = method.getParamList().get(pSize - 1);
      } else {
        p = method.getParamList().get(i);
      }
      if (!this.compareGive(fv.getObjType(), p.objTypeName)) {
        return false;
      }
    }
    return true;
  }

  public boolean compareGiveFromJavaObj(Class cls, String toName) {
    if (compareGive(cls.getSimpleName(), toName)) {
      return true;
    }
    if (cls.getSuperclass() != null) {
      return compareGiveFromJavaObj(cls, toName);
    }
    return false;
  }

  // 对比数据类型 从fromName 赋值给 toName 是否允许
  public boolean compareGive(String fromName, String toName) {
    if (fromName == null || toName == null) {
      return false;
    }
    if (toName.equals(fromName)) {
      return true;
    }
    if (toName.equals("Object")) {
      return true;
    }
    if (toName.equals("Class") && fromName.equals("FaClass")) {
      return true;
    }
    String aliName = aliaMap.get(fromName);
    if (aliName != null && aliName.equals(toName)) {
      return true;
    }
    if ((toName.equals("bool") || toName.equals("boolean") || toName.equals("Boolean"))
        && (fromName.equals("bool") || fromName.equals("boolean") || fromName.equals("Boolean"))) {
      return true;
    }
    if (toName.equals("Serializable") && this.compreSerializable(fromName)) {
      return true;
    }
    // 泛型
    if (fromName.contains("<") || toName.contains("<")) {
      String preFrom = fromName;
      if (fromName.contains("<")) {
        preFrom = AC.str.pre(fromName, "<");
      }
      String preTo = toName;
      if (toName.contains("<")) {
        preTo = AC.str.pre(toName, "<");
      }
      if (!this.compareGive(preFrom, preTo)) {
        return false;
      }
      String afterFrom = AC.str.suf(fromName, "<");
      String afterTo = AC.str.suf(toName, "<");
      if (afterFrom.equals(">") || AC.empty(afterFrom) || afterTo.equals(afterFrom) || AC.empty(afterTo)) {
        return true;
      }
      afterTo = afterTo.substring(0, afterTo.length() - 1);
      afterFrom = afterFrom.substring(0, afterFrom.length() - 1);
      if (afterTo.contains("extends")) {
        afterTo = AC.str.suf(afterTo, " ");
      }
      if (this.plat.isWidlyType(afterTo)) {
        return true;
      }
      if (this.compareGive(afterFrom, afterTo)) {
        return true;
      }
      return false;
    } else {
      FaConf conf = this.plat.getConf().getTypeExtend().get(toName);
      if (conf != null && this.isChildClass(conf, fromName)) {
        return true;
      }
    }
    // 父类
    FaClassConf fana = this.plat.getClassConf(fromName);
    if (fana != null) {
      if (fana.extendName != null && !fana.extendName.equals("Object")) {
        if (this.compareGive(fana.extendName, toName)) {
          return true;
        }
      }
      if (!AC.empty(fana.implNameList)) {
        for (String impName : fana.implNameList) {
          if (this.compareGive(impName, toName)) {
            return true;
          }
        }
      }
    }
    return false;
  }

  public boolean isChildClass(FaConf conf, String name) {
    for (FaConf item : conf.getChilds()) {
      if (item.getName().equals(name)) {
        return true;
      }
      if (item.getChilds() != null) {
        if (this.isChildClass(item, name)) {
          return true;
        }
      }
    }
    return false;
  }

  boolean compreSerializable(String fromName) {
    if ("int".equals(fromName)) {
      return true;
    }
    return false;
  }

  public boolean isSqlDao() {
    return "1".equals(this.def.get("Dao"));
  }

  public boolean isFatisDao() {
    return "dao".equals(this.def.get("FaBean.type"));
  }

  public boolean isCtrl() {
    return "ctrl".equals(this.def.get("FaBean.type"));
  }

  public boolean isBiz() {
    return this.def.get("FaBean") != null;
  }

  public boolean isSpringBean() {
    return isCtrl() || isBiz() || isSqlDao() || isFatisDao();
  }

  public FaClass instance(CodeLevel lvl, Object... pars) throws FaException {
    FaClass faClass = new FaClass(this.plat);
    faClass.setConf(this);
    SageStone thisSage = new SageStone(this.plat, null, faClass);
    faClass.setThisSage(thisSage);
    this.initClass(faClass, thisSage, lvl);
    if (pars != null && pars.length > 0) {
      List<FaValue> params = new ArrayList<>();
      for (Object obj : pars) {
        params.add(new FaValue(obj));
      }
      FaMethod method = faClass.conf.getMethod(faClass.conf.name, params);
      if (method != null) {
        faClass.call(method, params, lvl);
      }
    } else {
      FaMethod method = faClass.conf.getMethod(faClass.conf.name, null);
      if (method != null) {
        faClass.call(method, null, lvl);
      }
    }
    return faClass;
  }

  public FaClass instance(CodeLevel lvl) throws FaException {
    FaClass faClass = new FaClass(this.plat);
    faClass.setConf(this);
    SageStone thisSage = new SageStone(this.plat, null, faClass);
    faClass.setThisSage(thisSage);
    this.initClass(faClass, thisSage, lvl);
    return faClass;
  }

  public void initClass(FaClass fc, SageStone st, CodeLevel lvl) throws FaException {
    if (this.extendName != null) {
      FaClassConf fcc = this.plat.getClassConf(this.extendName);
      if (fcc != null) {
        fcc.initClass(fc, st, lvl);
      }
    }
    CodeLevel cl = null;
    if (lvl != null) {
      cl = lvl.instanceDeep();
    }
    st.run(this.initLogicList, cl);
  }

  public void initStatic() throws FaException {
    FaClass faClass = new FaClass(this.plat);
    faClass.setConf(this);
    faClass.setStatic(true);
    SageStone st = new SageStone(this.plat, staticLogicList, faClass);
    this.staticSage = st;
    st.run(null);
  }

  public String getAtTxt(String atCode) {
    if (this.getDef() == null) {
      return "";
    }
    String txt = this.getDef().get(atCode);
    if (txt == null) {
      txt = "";
    }
    return txt;
  }

  // 获取变量的注解
  public String getVarAtTxt(String fieldName, String atKey) {
    Map<String, String> def = varDef.get(fieldName);
    if (def != null) {
      String val = def.get(atKey);
      if (val != null) {
        return val;
      }
    }
    return "";
  }

  public FaPlatform getPlat() {
    return this.plat;
  }

  public void setPlat(FaPlatform plat) {
    this.plat = plat;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getExtendName() {
    return this.extendName;
  }

  public void setExtendName(String extendName) {
    this.extendName = extendName;
  }

  public List<String> getImplNameList() {
    return this.implNameList;
  }

  public void setImplNameList(List<String> implNameList) {
    this.implNameList = implNameList;
  }

  public List<Logic> getInitLogicList() {
    return this.initLogicList;
  }

  public void setInitLogicList(List<Logic> initLogicList) {
    this.initLogicList = initLogicList;
  }

  public List<Logic> getStaticLogicList() {
    return this.staticLogicList;
  }

  public void setStaticLogicList(List<Logic> staticLogicList) {
    this.staticLogicList = staticLogicList;
  }

  public Map<String, String> getDef() {
    return this.def;
  }

  public void setDef(Map<String, String> def) {
    this.def = def;
  }

  public Map<String, Map<String, String>> getVarDef() {
    return this.varDef;
  }

  public void setVarDef(Map<String, Map<String, String>> varDef) {
    this.varDef = varDef;
  }

  public List<FaMethod> getMethodList() {
    return this.methodList;
  }

  public void setMethodList(List<FaMethod> methodList) {
    this.methodList = methodList;
  }

  public SageStone getStaticSage() {
    return this.staticSage;
  }

  public void setStaticSage(SageStone staticSage) {
    this.staticSage = staticSage;
  }

  public Map<String, FaClassConf> getInner() {
    return this.inner;
  }

  public void setInner(Map<String, FaClassConf> inner) {
    this.inner = inner;
  }
}